'''
Created on 26.7.2011

@author: gilead
'''
from math import sin, cos, radians, pow, sqrt
from array import array

from panda3d.core import GeomVertexData, GeomVertexFormat,GeomVertexWriter,GeomTriangles,GeomNode,Geom,NodePath,TransparencyAttrib,Vec4,GeomVertexRewriter #@UnresolvedImport
from direct.task import Task

#from panda3d.core import CollisionTraverser,CollisionNode,CollisionSegment,CollisionHandlerQueue#@UnresolvedImport


class ArcUpdater(object):
    def __init__(self, world):
        self.world = world
        taskMgr.add(self.updateArcsTask, "updateFiringArcs")
        
        
    def updateArcsTask(self, task):
        for worldchild in self.world.getChildren():
            ship = worldchild.getPythonTag('Ship')
            if ship != None:
                for shipchild in ship.getChildren():
                    turret = shipchild.getPythonTag('Turret')
                    if turret != None:
                        for turretchild in turret.getChildren():
                            if turretchild.node().isGeomNode():
                                # kaikki geomnodet on tulitusarkkeja
                                if turretchild.isHidden() == False:
                                    self.updateArc(turret, turretchild)
        return Task.cont
    
    def getOtherShipFromLine(self, x, y, turret, arc):
        myShip = turret.getParent().getPythonTag('Ship')
        otherShip = None
        collisionPos = None
        
        turret.line.setPointB(x,y,0)
        turret.traverser.traverse(self.world)
        for i in range(turret.queue.getNumEntries()):
            pickedObj = turret.queue.getEntry(i).getIntoNodePath()
            #getIntoNodePath palauttaa CollisionNode:n, joka on kiinnitetty actorin lapseksi
            pickedObj = pickedObj.findNetTag('selectableObject')
            if not pickedObj.isEmpty():
                ship = pickedObj.getPythonTag('Ship')
                if ship != None:
                    # ignoroidaan collision omaan laivaan
                    if ship != myShip:
                        otherShip = ship
                        collisionPos = turret.queue.getEntry(i).getSurfacePoint(arc)
                        break # ulos loopista
        return otherShip,collisionPos
    
    '''
    Paivittaa annettua tulitussektoria siten, etta ottaa huomioon muiden laivojen aiheuttamat katveet.
    
    Toisinsanottuna, mikali tulitussektorissa on objekti joka estaa ammusten lentamista, tulitussektori
    asetetaan ylettymaan vain tahan objektiin asti.
    '''
    def updateArc(self, turret, arc):
        beginAngle = turret.beginAngle
        endAngle = turret.endAngle
        length = turret.optimal
        diff = endAngle - beginAngle
        angleRad = radians(diff/Arc.numSteps)
        beginRad = radians(beginAngle) 
        
        xs = array('d')
        ys = array('d')
        blocked = array('b')
        for i in range(Arc.numSteps+1):
            a = beginRad + angleRad * i
            y = sin(a) * length
            x = cos(a) * length
            # tarkistetaan, osuuko tama viiva johonkin toiseen laivaan
            otherShip,collisionPos = self.getOtherShipFromLine(x,y,turret,arc)
            if otherShip != None:
                blocked.append(True)
                xs.append(collisionPos.getX())
                ys.append(collisionPos.getY())
            else:
                blocked.append(False)
                xs.append(x)
                ys.append(y)
        
        # laitetaan blocked-listaan yksi ylimaarainen alkio, jotta seuraavassa loopissa voidaan
        # yli-indeksoida sita ongelmitta
        if diff == 360:
            blocked.append(blocked[Arc.numSteps])
        else:
            blocked.append(False)
        
        # haetaan vertexdata ja luodaan siita uudelleenkirjoittaja
        geomNode = arc.node()
        geom = geomNode.modifyGeom(0)
        vdata = geom.modifyVertexData()
        vertex = GeomVertexRewriter(vdata, 'vertex')
        
        # ensimmaiseen alkioon ei tehda muutoksia
        v = vertex.getData3f()
        vertex.setData3f(v[0], v[1], v[2])
        
        # toista alkiota muutetaan hieman
        v = vertex.getData3f()
        vertex.setData3f(xs[0], ys[0], 0)
        
        for i in range(1,Arc.numSteps+1):
            if blocked[i] == True:
                # viiva oli blokattu, joten laitetaan molemmat pisteet tormayskohtaan
                vertex.setData3f(xs[i], ys[i], 0)
                vertex.setData3f(xs[i], ys[i], 0)
                continue
            if blocked[i] == False and blocked[i-1] == True:
                # edellinen viiva oli blokattu, mutta nykyinen ei, joten lasketaan pisteen kohta
                # tehdaan edeltavan viivan pituinen viiva
                length = sqrt(pow(xs[i-1], 2) + pow(ys[i-1],2))
                # mutta nykyisen viivan suuntaan
                a = beginRad + angleRad * i
                y = sin(a) * length
                x = cos(a) * length
                vertex.setData3f(x,y,0)
            else:
                vertex.setData3f(xs[i], ys[i], 0)
            
            if blocked[i] == False and blocked[i+1] == True:
                # seuraava viiva on blokattu mutta nykyinen ei, joten lasketaan pisteen kohta
                # tehdaan edeltavan viivan pituinen viiva
                length = sqrt(pow(xs[i+1], 2) + pow(ys[i+1],2))
                # mutta nykyisen viivan suuntaan
                a = beginRad + angleRad * i
                y = sin(a) * length
                x = cos(a) * length
                vertex.setData3f(x,y,0)
            else:
                vertex.setData3f(xs[i], ys[i], 0)

class Arc(object):
    # taman avulla pyritaan luomaan jokaiselle wedgelle oma zdepth-taso
    # TODO: tee alue isommaksi tms
    zdepth = 0
    
    numSteps = 20 # ala muuta ohjelman ollessa kaynnissa, tai updateArc alkaa toimimaan hassusti
    # vaikuttaa paljon paivityksen hitauteen
        
    @staticmethod
    def makeArc(beginAngle=0, endAngle=360, length=10):
        vdata = GeomVertexData('fan', GeomVertexFormat.getV3(), Geom.UHDynamic)
        # TODO: mikali tulevaisuudessa emme muuta verteksien arvoja, dynamicin voi vaihtaa staticiksi
        vertex = GeomVertexWriter(vdata, 'vertex')
        #color = GeomVertexWriter(vdata, 'color')
        
        diff = endAngle - beginAngle
        #numSteps = int(ceil(diff / stepSize))
        angleRad = radians(diff/Arc.numSteps)
        beginRad = radians(beginAngle)
        
        vertex.addData3f(0,0,0)
        #color.addData4f(0, 0, 1, 1)
        vertex.addData3f(cos(beginRad) * length, sin(beginRad) * length, 0)
        
        for i in range(Arc.numSteps+1):
            a = beginRad + angleRad * i
            y = sin(a) * length
            x = cos(a) * length
            
            # syotetaan tuplat, koska myohemmin jompaakumpaa tai molempia saatetaan muuttaa
            vertex.addData3f(x, y, 0)
            vertex.addData3f(x, y, 0)
            #color.addData4f(0, 0, 1, 1)
            
        prim = GeomTriangles(Geom.UHStatic)
        
        for i in range(1, Arc.numSteps*2+1,2):
            prim.addVertices(0, i, i+1)
            prim.closePrimitive()
        
        geom = Geom(vdata)
        geom.addPrimitive(prim)
         
        node = GeomNode('gnode')
        #node.setEffect(DecalEffect.make())
        # TODO: jos saat decaleffectin toimimaan, siistia!
        node.addGeom(geom)
        
        np = NodePath(node)
        np.setColor(Vec4(1, 0, 1, 0.5))
        np.setTwoSided(True)
        np.setTransparency(TransparencyAttrib.MAlpha)
        np.setDepthOffset(Arc.zdepth)
        Arc.zdepth = Arc.zdepth + 1
        return np
    
    @staticmethod
    def makeArc2(beginAngle=0, endAngle=360, length=10):
        vdata = GeomVertexData('fan', GeomVertexFormat.getV3(), Geom.UHDynamic)
        # TODO: mikali tulevaisuudessa emme muuta verteksien arvoja, dynamicin voi vaihtaa staticiksi
        vertex = GeomVertexWriter(vdata, 'vertex')
        
        diff = endAngle - beginAngle
        
        angleRad = radians(diff/Arc.numSteps)
        beginRad = radians(beginAngle)
        
        vertex.addData3f(0,0,0)
        
        for i in range(Arc.numSteps+1):
            a = beginRad + angleRad * i
            y = sin(a) * length
            x = cos(a) * length
            
            # syotetaan tuplat, koska myohemmin jompaakumpaa tai molempia saatetaan muuttaa
            vertex.addData3f(x, y, 0)
            
        prim = GeomTriangles(Geom.UHStatic)
        
        for i in range(1, Arc.numSteps+1):
            prim.addVertices(0, i, i+1)
            prim.closePrimitive()
        
        geom = Geom(vdata)
        geom.addPrimitive(prim)
         
        node = GeomNode('gnode')
        #node.setEffect(DecalEffect.make())
        # TODO: jos saat decaleffectin toimimaan, siistia!
        node.addGeom(geom)
        
        np = NodePath(node)
        np.setColor(Vec4(1, 0, 1, 0.5))
        np.setTwoSided(True)
        np.setTransparency(TransparencyAttrib.MAlpha)
        np.setDepthOffset(Arc.zdepth)
        Arc.zdepth = Arc.zdepth + 1
        return np